package com.lqian.case_lock;

import com.lqian.utils.SleepHelper;

/**
 * 测试死锁：
 * 两个线程相互得到锁1，锁2
 * 然后线程1等待线程2释放锁2，线程2等待线程1释放锁1
 * 两者各不相让，这样形成了死锁
 *
 * 死锁的概念：
 * 一个线程需要锁住多个共享资源。
 * 并不是只有一个线程设计多个锁才会出现死锁，单个锁也有可能出现死锁
 * 1、加锁之后没有释放锁 lock/unlock
 * 2、两个锁A和B，线程1持有A然后等待B，与此同时线程1持有B然后等待A。
 *    两个线程都不释放拥有的锁，也都获取不到等待的锁
 *---------------------------------------------
 * 那么如何避免和解决死锁问题呢？
 * 尽量不要在同一个线程中同时锁住两个临界资源，不过如果业务要求这样，那就没办法
 *
 *
 * 1、按顺序加锁
 * 线程间加锁的顺序各不一致，导致死锁，如果每个线程都按同一个的加锁顺序，就不会出现死锁
 * 多个线程对多个锁的加锁顺序一样这样就不会发生死锁。
 * 比如线程1对A资源加锁，再对B资源加锁
 * 线程2也使用相同的顺序，就不会产生死锁
 * 2、获取锁时限
 * 在获取锁的时候加上一个时限，如果超过某个时间就放弃获取锁
 * 3、死锁检测
 * 4、一次性获取所有需要的锁，如果不能一次性获取则等待
 * 按线程间获取锁的关系顺序，检测线程间是否发生死锁，如果发生死锁就执行一定的策略
 * 如：中断线程或回滚异常
 * @author：Melody_LQ
 * @time：2022/9/1 10:44 下午
 */
public class TestDeadLock {
    //平平无奇的两把锁
    //第一个条件：互斥条件
    //static 变量 在程序运行中，只有一个
    // synchronized中的锁 lock1，lock2只能被一个线程所持有
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static void main(String[] args) {

        //线程1
        new Thread(()->{
            // 第二个条件：请求与保持
            // 线程1保持锁lock1,请求锁lock2
            // 线程2 保持锁 lock2，请求锁lock1
            // 并且synchronized的特点就是没有获取到锁的时候，也不会去释放锁
            synchronized (lock1){
                System.out.println("Thread1 get lock1");
                //线程1持有所lock1，等待1s，让线程2执行
                SleepHelper.sleepSeconds(1);
                //线程1尝试去获取lock2
                synchronized (lock2){
                    System.out.println("Thread1 get lock2");
                }
            }
            System.out.println("thread1 end");
        }).start();

        //线程2
        new Thread(()->{
            //lock2 -> lock1 每个线程按照同样的顺序加锁
            synchronized (lock2){
                System.out.println("Thread2 get lock2");
                SleepHelper.sleepSeconds(1);
                synchronized (lock1){
                    System.out.println("Thread2 get lock1");
                }
            }
            System.out.println("thread2 end");
        }).start();
    }
    // 第三个条件：不剥夺
    // 相互等待对方的锁资源的时候，不会有额外的力量去剥夺某一方的锁，只会让他们继续等待下去
    // 第四个条件：循环等待条件
    // 两个线程的时候，线程1与线程2相互等待，多个线程的时候，形成环路的等待

}
